package com.oss.util;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 雪花算法生成ID
 * @author chengqiang
 */
public class SnowFlakeUtil {

    private static final SnowFlakeUtil snowFlakeUtil;
    static {
        snowFlakeUtil = new SnowFlakeUtil();
    }
    //数据中心id
    private static final long datacenterId=1;
    //数据中心id位数
    private static final long datacenterBits=5;
    //机器id
    private static final long workerId=1;
    //机器id位数
    private static final long workerBits=5;
    //序列id所占位数
    private static final  long sequenceBits=12;
    // 时间戳起始点（毫秒）
    private static final long twepoch = 1704038400000L;

    //数据中心最大id
    private static final long maxDatacenterId = -1L ^ (-1L << datacenterBits);
    //机器最大id
    private static final long maxWorkerId = -1L ^ (-1L << workerBits);
    //最大序列号
    private static final long maxSequence = -1L ^ (-1L << sequenceBits);
    //机器id左移位数
    private static final long workerIdShift = sequenceBits;
    //数据中心id左移位数
    private static final long datacenterIdShift = sequenceBits + workerBits;
    //毫秒数左移位数
    private static final long timestampLeftShift = sequenceBits + workerBits + sequenceBits;
    //单次批量生成id的最大数量
    private final int maxBatchCount=100000;
    // 序列号
    private static long sequence = 0L;
    // 上一次时间戳
    private static long lastTimestamp = -1L;

    /**
     * id生成方法(单个)
     * @return 返回ID
     */
    public static synchronized long nextId() {
        //获取当前时间的毫秒数
        long timestamp = currentTime();

        //判断时钟是否回拨
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("时钟回拨，回拨毫秒数：%d", lastTimestamp - timestamp));
        }

        //设置序列号
        if (lastTimestamp == timestamp) {
            //设置序列号递增，如果当前毫秒内序列号已经达到最大值，则直到下一毫秒在重新从0开始计算序列号
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        //计算id
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    /**
     * id生成方法(批量)
     * @return 批量生成
     */
    public synchronized List<Long> nextIds(int count) {
        if (count > maxBatchCount || count < 0) {
            throw new IllegalArgumentException(String.format("批量生成id的数量不能大于%d或小于0", maxBatchCount));
        }

        List<Long> ids = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            ids.add(nextId());
        }
        return ids;
    }

    /**
     * 循环等待直至获取到新的毫秒时间戳
     * 确保生成的时间戳总是向前移动的，即使在相同的毫秒内请求多个ID时也能保持唯一性。
     */
    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = currentTime();
        // 循环等待直至获取到新的毫秒时间戳
        while (timestamp <= lastTimestamp) {
            timestamp = currentTime();
        }
        return timestamp;
    }

    /**
     * 获取当前时间的毫秒数
     */
    private static long currentTime() {
        return System.currentTimeMillis();
    }

    /**
     * 获取ID
     * @return 返回ID
     */
    public static String getID() {
        //return String.valueOf(Math.abs(nextId()));
        // 要求改成UUID
        return UUID.randomUUID().toString().replaceAll("-","");
    }
    public static void main(String[] args) {
        System.out.println(nextId());
    }

}